package sunyu.toolkit.core;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.nutz.json.Json;
import org.nutz.mapl.Mapl;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.List;

/**
 * JSON工具类
 *
 * @author 孙宇
 */
public class JsonKit {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    static {
        objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);//允许json字符串属性没有双引号
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);//允许json字符串属性是单引号包裹
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));//设置日期格式
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);//属性为""或者为NULL都不进行序列化
    }

    /**
     * 对象转Json字符串
     *
     * @param obj 对象
     * @return json字符串
     */
    public static String toJson(Object obj) {
        return toJson(obj, true);
    }

    /**
     * 对象转Json字符串
     *
     * @param obj    对象
     * @param indent 是否启用紧凑模式
     * @return json字符串
     */
    public static String toJson(Object obj, boolean indent) {
        if (indent) {
            objectMapper.disable(SerializationFeature.INDENT_OUTPUT);//禁用美化输出
        } else {
            objectMapper.enable(SerializationFeature.INDENT_OUTPUT);//开启美化输出
        }
        try {
            return objectMapper.writeValueAsString(obj);
        } catch (IOException e) {
            System.out.println(ExceptionKit.getStackTrace(e));
        }
        return null;
    }

    /**
     * 对象转Json字符串
     *
     * @param json   json字符串
     * @param toType 转换成什么类型
     * @param <T>    类型泛型
     * @return 对象
     */
    public static <T> T toObject(String json, Class<T> toType) {
        try {
            return objectMapper.readValue(json, toType);
        } catch (IOException e) {
            System.out.println(ExceptionKit.getStackTrace(e));
        }
        return null;
    }

    /**
     * Json字符串转换List
     *
     * @param json        json字符串
     * @param entityClass 转换成什么类型
     * @param <T>         类型泛型
     * @return 对象列表
     */
    public static <T> List<T> toList(String json, Class<T> entityClass) {
        return toList(json, Collection.class, entityClass);
    }

    /**
     * Json字符串转换List
     *
     * @param json            json字符串
     * @param collectionClass 集合类型
     * @param entityClass     项目类型
     * @param <T>             泛型
     * @return 对象集合
     */
    public static <T> List<T> toList(String json, Class<?> collectionClass, Class<T> entityClass) {
        try {
            return objectMapper.readValue(json, objectMapper.getTypeFactory().constructParametricType(collectionClass, entityClass));
        } catch (IOException e) {
            System.out.println(ExceptionKit.getStackTrace(e));
        }
        return null;
    }

    /**
     * 通过路由获得属性
     * <p>
     * path的规则：
     * <p>
     * map的值访问直接使用 '.', 如: abc.name<p>
     * list的访问使用 "名称[索引]", 如: as[1]. 当然要是不想写[]也可以使用 as.1.name的形式.<p>
     * 顶层为list时, 使用 "[索引].其它", 如: [1].name<p>
     * 如果想得到一个List, 而不是它某个值, 则可以使用 "名称" 不加 "[索引]". 如: as<p>
     * 如果List后加了"[]"中间却没有索引, 则默认访问第一个元素, 如: user[] 等效 user[1]<p>
     *
     * @param json        json字符串
     * @param path        路由
     * @param entityClass 要转换成什么类型
     * @param <T>         泛型
     * @return 对象
     */
    public static <T> T getProperty(String json, String path, Class<T> entityClass) {
        Object obj = Json.fromJson(json);//转换成Mapl结构
        return (T) Mapl.cell(obj, path);
    }

    /**
     * 通过路由获得属性
     * <p>
     * path的规则：
     * <p>
     * map的值访问直接使用 '.', 如: abc.name<p>
     * list的访问使用 "名称[索引]", 如: as[1]. 当然要是不想写[]也可以使用 as.1.name的形式.<p>
     * 顶层为list时, 使用 "[索引].其它", 如: [1].name<p>
     * 如果想得到一个List, 而不是它某个值, 则可以使用 "名称" 不加 "[索引]". 如: as<p>
     * 如果List后加了"[]"中间却没有索引, 则默认访问第一个元素, 如: user[] 等效 user[1]<p>
     *
     * @param obj         对象
     * @param path        路由
     * @param entityClass 要转换成什么类型
     * @param <T>         泛型
     * @return 对象
     */
    public static <T> T getProperty(Object obj, String path, Class<T> entityClass) {
        return getProperty(JsonKit.toJson(obj), path, entityClass);
    }

}
